#ifndef __VANILA_ALLOCATOR_HH__
#define __VANILA_ALLOCATOR_HH__

#include "vanila/value.h"
#include "utils/singleton.h"
#include <string>
#include <vector>
#include <map>
#include <set>

namespace vanila
{

class Allocator : utils::Singleton<Allocator>
{
    enum { GC_HEAP_GROW_FACTOR = 2 };
    
public:
    //! \brief constructor and destructor
    Allocator() noexcept;
    ~Allocator() noexcept = default;

public:
    //! \brief allocate a ObjectBoundMethod instance
    ObjectBoundMethod* allocateBoundMethod(const Value& receiver, Object* method);

    //! \brief allocate a ObjectClass instance
    ObjectClass* allocateClass(ObjectString* name, bool isNative = false);

    //! \brief allocate a ObjectClosure instance
    ObjectClosure* allocateClosure(ObjectFunction* function);

    //! \brief allocate a ObjectDictionay instance
    ObjectDictionary* allocateDictionary();
    ObjectDictionary* allocateDictionary(Value* slot, uint16_t pairSize);
    
    //! \brief allocate a ObjectFunction instance
    ObjectFunction* allocateFunction();

    //! \brief allocate a ObjectInstance instance
    ObjectInstance* allocateInstance(ObjectClass* klass);

    //! \brief allocate a ObjectList instance
    ObjectList* allocateList();
    ObjectList* allocateList(Value* slot, uint16_t elementSize);
    ObjectList* allocateList(std::vector<Value> list);

    //! \brief allocate a ObjectFunction instance
    ObjectUpvalue* allocateUpvalue(Value* slot);

    //! \brief allocate a ObjectFunction instance
    ObjectNative* allocateNative(NativeFunction function);

    //! \brief allocate a ObjectString instance
    //! \note before new a ObjectString instance, check wheathe this string is in the vm's stirng hashtable
    ObjectString* allocateString(const char* chars, uint32_t length);
    ObjectString* allocateString(std::string str);

    //! \brief allocate a ObjectSet instance
    ObjectSet* allocateSet();
    ObjectSet* allocateSet(Value* slot, uint16_t elementSize);

    //! \brief deallocate the object instance
    void deallocateObject(Object* object) noexcept;

private:
    //! \brief malloc or free the memory
    void* allocate(size_t size);
    void deallocate(void* ptr, size_t size) noexcept;

private:
    size_t _bytesAlloceted;
    size_t _nextGC;
};

}

#endif